/*
 * Generic receive layer for the PXA USB client function
 *
 * This code was loosely inspired by the original version which was
 * Copyright (c) Compaq Computer Corporation, 1998-1999
 * Copyright (c) 2001 by Nicolas Pitre
 *
 * (C) Copyright 2006 Marvell International Ltd.
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * 02-May-2002
 *   Frank Becker (Intrinsyc) - derived from sa1100 usb_recv.c
 *
 * TODO: Add support for DMA.
 *
 */

#include "types.h"
#include "arch.h"
#include "time.h"
#include "mhn-regs.h"
#include "xllp_u2d.h"
#include "mhn_u2d_ctl.h"
#include "util.h"
#include "asm/errno.h"


#define MHN_U2D_EP2_DBG 0
#if MHN_U2D_EP2_DBG
#define DBGOUT(x) x
#else
#define DBGOUT(x)     /* gcc will remove all the debug code for us */
#endif

#define DELAY() do { \
        __asm__ volatile (\
        "mov ip, #1000\n"\
        "1: mov r0, r0\n"\
        "sub ip, ip, #1\n"\
        "cmp ip, #0\n"\
        "bne    1b\n");\
    }while(0);

extern XLLP_U2D_T   xllp_u2d;
void dump_info(int ep_num);
void u2d_int_hndlr(int irq, void *dev_id);
extern int arp_t;
int req_pending = 0;

#define DMA_MAX_TRANS 1536

#ifndef MMU_ENABLE
static char ep2_buf[1536];
#else
static char *callback_buf;
extern char *ep2_buf;
#endif
static int   ep2_len = 0;
static int   ep2_index = 0;
static int   ep2_rx_done = 0;
static int   ep2_dma_stop = 1;
usb_callback_t ep2_callback=0;
P_XLLP_DMAC_DESCRIPTOR_T    ep2_dma_desc;

static P_XLLP_U2D_EP_CONFIG ep2_config = &xllp_u2d.ConfigTable[2];
static P_XLLP_U2D_TRANSFER ep2_txfr = &xllp_u2d.u2dEpXferTable[2];

void ep2_recv_packet(void)
{
    DBGOUT(printf("#######In ep2_recv_packet!U2DCSRB = %x\n", U2DCSRB));
    __u32 dcsr;

    if (ep2_dma_stop) {
        XllpU2DEnableInterrupt(&xllp_u2d, 2, U2D_DMA_EORSTOP);
        XllpU2DDisableInterrupt(&xllp_u2d, 2, U2D_PACKET_COMPL_INT);
        DBGOUT(printf("ep2_recv_packet>>, U2DMACSRB %x\n", U2DMACSR(2)));

        U2DMACSR(2) |= U2D_DMA_EOR;
        U2DMACSR(2) &= ~(U2D_DMA_EORSTOP);
        DBGOUT(printf("ep2_recv_packet>>>, U2DMACSRB %x\n", U2DMACSR(2)));

        U2DCSRB |= U2DCSR_FEF;
        DBGOUT(printf("after flush fifo B, U2DCSRB = %x, dmacsrB %x\n", U2DCSRB, U2DMACSR(2)));
        U2DCSRB |= U2DCSR_DME;
        DBGOUT(printf("after enable DMA for B, U2DCSRB = %x\n", U2DCSRB));
        U2DMACSR(2) &= ~U2DMACSR_RUN;
        DBGOUT(printf("u2dcr = %x, csrB = %x, dmacsrB = %x\n", U2DCR, U2DCSRB, U2DMACSR(2)));

        /* init the params for transfer */
        ep2_txfr->direct = 0;
        ep2_txfr->physicalAddress = (XLLP_UINT32_T)ep2_buf;
        ep2_txfr->EnableEndIrq = 1;
        ep2_txfr->isZeroPackage = (ep2_len==0)?1:0;
        ep2_txfr->len = DMA_MAX_TRANS;
        ep2_txfr->udcEndpointNum = 2;

        ep2_config->vDmaDescriptor = ep2_dma_desc;
        ep2_config->pDmaDescriptor = ep2_dma_desc;
        ep2_config->nDescriptors = DMA_DESC_NUM/2;


        DBGOUT(printf("dmadescriptor virtual address: %x, physical address: %x\n", ep2_config->vDmaDescriptor, ep2_config->pDmaDescriptor));

        XllpU2DStartTransfer(&xllp_u2d, ep2_txfr);
        DBGOUT(printf("before run!dmacsr = %x, csrB = %x\n", U2DMACSR(2), U2DCSRB));

        dcsr = U2DMACSR(2);
        dcsr &= U2DMACSR_MASK;
        dcsr |= U2DMACSR_RUN;

        U2DMACSR(2) = dcsr;

        ep2_dma_stop = 0;
    }

    ep2_rx_done = 0;
    DBGOUT(printf("after run!isr %x u2dmacsrB %x u2dcsrB %x\nwait for dma interrupt.......\n", U2DISR, U2DMACSR(2), U2DCSRB));
    do{
        if ( ICPR2 & 0x4000 ) {
            u2d_int_hndlr(0x11, 0);
        }
        if(ep2_rx_done) break;
    }while (1);

    DBGOUT(printf("end  of  transfer\n"));
}

static void ep2_start(void)
{
    DBGOUT(printf("###########In ep2_start\n"));

    DBGOUT(printf("ep2_start>, U2DMACSRB %x\n", U2DMACSR(2)));

    ep2_recv_packet();
}

void ep2_done(int flag)
{
    if (!ep2_len)
        return;

    ep2_len = 0;
    if ((int)ep2_callback!=0xffffffff) {
#ifdef MMU_ENABLE
        DBGOUT(printf("ep2_callback %x\n",ep2_callback));
        memcpy(callback_buf, ep2_buf, size);
#endif
    }
}

void ep2_state_change_notify( int new_state )
{
    ep2_len = 0;
    ep2_index = 0;
    ep2_rx_done = 0;
    ep2_dma_stop = 1;
}

void ep2_stall( void )
{
    /* SET_FEATURE force stall at UDC */
    /* UDCCS2 |= UDCCS_BO_FST; */
}

void ep2_reset(void)
{
    DBGOUT(printf("In ep2_reset!\n"));

    ep2_done(-EINTR);
}

extern int usb_connected;

void ep2_int_hndlr(int udcsr)
{
    DBGOUT(printf("######In ep2_int_hndlr\n"));
    int dcmd, dadr;
    unsigned int desc_num/*, length, count*/;
    DBGOUT(printf("ep2_int_hndlr: dcsr=%x, dcmd=%x, ddad=%x\n", U2DMACSR(2), U2DMACMDR(2), U2DMADADR(2)));

    if (U2DMACSR(2) & U2DMACSR_EORINTR) {
        DBGOUT(printf("U2DMACSR_EORINTR interrupt!\n"));
        U2DMACSR(2) &= ~U2DMACSR_EORINTR;

        DBGOUT(printf("STOP DMA!\n"));
        U2DMACSR(2) &= ~U2DMACSR_RUN;

        /* caculate the data length */
        dcmd = U2DMACMDR(2);
        dadr = U2DMADADR(2);
        DBGOUT(printf("dcmd %x, dadr %x\n", dcmd, dadr));
        desc_num = ((unsigned)(dadr&0xfffffff0) - (unsigned)&ep2_dma_desc[0])/16;
        ep2_len = ep2_config->MPS*desc_num - (dcmd & U2DMACMDR_LEN);
        DBGOUT(printf("ep2_len = %d\n", ep2_len));
        ep2_index = 0;
        ep2_rx_done = 1;
    }

    //restart the dma for the next data transfer
    if (!(U2DMACSR(2) & U2DMACSR_REQPEND)) {
        DBGOUT(printf("Now prepare for restart dma\n"));
        /* init the params for transfer */
        ep2_txfr->direct = 0;
        ep2_txfr->physicalAddress = (XLLP_UINT32_T)ep2_buf;
        ep2_txfr->EnableEndIrq = 1;
        ep2_txfr->isZeroPackage = 0;
        ep2_txfr->len = DMA_MAX_TRANS;
        ep2_txfr->udcEndpointNum = 2;

        ep2_config->vDmaDescriptor = ep2_dma_desc;
        ep2_config->pDmaDescriptor = ep2_dma_desc;
        ep2_config->nDescriptors = DMA_DESC_NUM/2;

        DBGOUT(printf("dmadescriptor virtual address: %x, physical address: %x\n", ep2_config->vDmaDescriptor, ep2_config->pDmaDescriptor));

        XllpU2DStartTransfer(&xllp_u2d, ep2_txfr);

        DBGOUT(printf("RESTART DMA!\n"));
        U2DMACSR(2) |= U2DMACSR_RUN;
    }


    if (U2DMACSR(2) & U2DMACSR_STOPINTR) {
       DBGOUT(printf("U2DMACSR_STOPINTR interrupt!\n"));
        U2DMACSR(2) &= ~U2DMACSR_STOPINTR;
    }

    if (U2DMACSR(2) & U2DMACSR_BUSERRINTR) {
        DBGOUT(printf("U2DMACSR_BUSERRINTR interrupt!\n"));
        U2DMACSR(2) &= ~U2DMACSR_BUSERRINTR;
    }

    DBGOUT(printf("end of ep2_int_hndlr: dcsrB=%x, csrB %x, dcmdB=%x, ddadB=%x\n", U2DMACSR(2), U2DCSRB, U2DMACMDR(2), U2DMADADR(2)));

    return;
}

int ep2_recv(char *buf, int len)
{
    DBGOUT(printf("###############In ep2_recv, len: %d, dmacsrB %x\n", len, U2DMACSR(2)));
    int recv_len;
    if (!ep2_len) {
        DBGOUT(printf("Not has data in buffer, to recv packet!\n"));
            ep2_start();
        }
    else
        DBGOUT(printf("Still has data in buffer:)\n"));

    recv_len = (ep2_len>len)? len: ep2_len;

    memcpy(buf, ep2_buf + ep2_index, recv_len);
    ep2_index += recv_len;
    ep2_len -= recv_len;

    return recv_len;
}

int ep2_begin(int flag)
{
    int dcsr = U2DMACSR(2);

#if 0
    if(!req_pending) {
        dcsr &= ~U2DMACSR_RUN;
        XllpU2DDisableInterrupt(&xllp_u2d, 2, U2D_DMA_STOP|U2D_DMA_RAS);

        U2DMACSR(2) = dcsr;

        while(U2DISR & (U2DINT_SPACKETCMP <<6)) {
            U2DISR = U2DINT_SPACKETCMP <<6;
            U2DCSRB = U2DCSR_FEF | U2DCSR_PC;
        }
        U2DCSRB = 0;
        XllpU2DDisableInterrupt(&xllp_u2d, 2, U2D_DMA_STOP);

        U2DCSRB = U2DCSR_DME;
    }
#endif
    XllpU2DEnableInterrupt(&xllp_u2d, 2, U2D_DMA_STOP/*|U2D_SHORT_PACKET_COMPL_INT*/|U2D_DMA_EORSTOP);

    dcsr = U2DMACSR(2);
    dcsr &= U2DMACSR_MASK;
    dcsr |= U2DMACSR_RUN;

    U2DMACSR(2) = dcsr;
    dcsr = U2DMACSR(2);
    dcsr = U2DCSRB;

    DBGOUT(printf("isr %x u2dmacsr2 %x u2dcsr2 %x\n", U2DISR, U2DMACSR(2), U2DCSRB));
    return 0;
}

int ep2_end(int flag)
{
    int dcsr = U2DMACSR(2);

    dcsr &= ~U2DMACSR_RUN;
    dcsr &= U2DMACSR_MASK;
    U2DMACSR(2) = dcsr;
    return 0;
}
